home *** CD-ROM | disk | FTP | other *** search
/ MacFormat UK 178 / MF_UK_178_1.iso / DiscContents / In the mag / Widgets / Expedia Deals 1.0 / Expedia Deals / Expedia Deals.wdgt / Fader.js < prev    next >
Encoding:
JavaScript  |  2005-06-26  |  9.0 KB  |  305 lines

  1. /*
  2.  
  3. File: Fader.js
  4.  
  5. Abstract: JavaScript logic for one-off and transitional fades of DOM elements 
  6.  
  7. Version: 1.0
  8.  
  9. ¬© Copyright 2005 Apple Computer, Inc. All rights reserved.
  10.  
  11. IMPORTANT:  This Apple software is supplied to 
  12. you by Apple Computer, Inc. ("Apple") in 
  13. consideration of your agreement to the following 
  14. terms, and your use, installation, modification 
  15. or redistribution of this Apple software 
  16. constitutes acceptance of these terms.  If you do 
  17. not agree with these terms, please do not use, 
  18. install, modify or redistribute this Apple 
  19. software.
  20.  
  21. In consideration of your agreement to abide by 
  22. the following terms, and subject to these terms, 
  23. Apple grants you a personal, non-exclusive 
  24. license, under Apple's copyrights in this 
  25. original Apple software (the "Apple Software"), 
  26. to use, reproduce, modify and redistribute the 
  27. Apple Software, with or without modifications, in 
  28. source and/or binary forms; provided that if you 
  29. redistribute the Apple Software in its entirety 
  30. and without modifications, you must retain this 
  31. notice and the following text and disclaimers in 
  32. all such redistributions of the Apple Software. 
  33. Neither the name, trademarks, service marks or 
  34. logos of Apple Computer, Inc. may be used to 
  35. endorse or promote products derived from the 
  36. Apple Software without specific prior written 
  37. permission from Apple.  Except as expressly 
  38. stated in this notice, no other rights or 
  39. licenses, express or implied, are granted by 
  40. Apple herein, including but not limited to any 
  41. patent rights that may be infringed by your 
  42. derivative works or by other works in which the 
  43. Apple Software may be incorporated.
  44.  
  45. The Apple Software is provided by Apple on an "AS 
  46. IS" basis.  APPLE MAKES NO WARRANTIES, EXPRESS OR 
  47. IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED 
  48. WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY 
  49. AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING 
  50. THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE 
  51. OR IN COMBINATION WITH YOUR PRODUCTS.
  52.  
  53. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY 
  54. SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL 
  55. DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  56. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
  57. OF USE, DATA, OR PROFITS; OR BUSINESS 
  58. INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, 
  59. REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF 
  60. THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER 
  61. UNDER THEORY OF CONTRACT, TORT (INCLUDING 
  62. NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN 
  63. IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF 
  64. SUCH DAMAGE.
  65.  
  66. */ 
  67.  
  68.  
  69. /*
  70.  *************************************************
  71.  * Fader object fades a single element in or out *
  72.  *************************************************
  73.  */
  74.  
  75. /*
  76.  * Fader constructor.  Parameters:
  77.  * - element: The element to fade in or out
  78.  * - callback: A function that will be called when a fade is complete
  79.  * - fadeTime: How long (in ms) the fade should take (see setFadeTime())
  80.  */
  81. function Fader (element, callback, fadeTime) {
  82.     this.element = element;
  83.     this.startTime = 0;
  84.     this.timer = null;
  85.     
  86.     this.doneNotification = callback;
  87.     
  88.     // Initialize for a fade-in; these values will be reset by the fadeIn/fadeOut functions
  89.     this.fadingIn = false;
  90.     this.now = 0.0;
  91.     this.from = 0.0;
  92.     this.to = 1.0;
  93.     
  94.     this.setFadeTime(fadeTime);
  95. }
  96.  
  97. /* 
  98.  * Prototype method declarations; call these methods as 
  99.  * nameOfFaderInstance.methodName();
  100.  */
  101. Fader.prototype.fadeOut = function () {
  102.     if (this.fadingIn) {
  103.         this.startFade(this.now, 0.0);
  104.         this.fadingIn = false;
  105.     }
  106. }
  107.  
  108. Fader.prototype.fadeIn = function () {
  109.     if (!this.fadingIn) {
  110.         this.startFade(this.now, 1.0);
  111.         this.fadingIn = true;
  112.     }
  113. }
  114.  
  115. Fader.prototype.startFade = function (newFrom, newTo) {
  116.     this.from = newFrom;
  117.     this.to = newTo;
  118.     
  119.     this.startTime = (new Date).getTime() - 13; // set it back one frame
  120.  
  121.     if (this.timer != null) {
  122.         clearInterval(this.timer);
  123.         this.timer = null;
  124.     }
  125.     
  126.     // Must store "this" in a local variable to call an object method in 
  127.     // a setInterval timer; this correctly binds the call to the current Fader
  128.     // object when the timer moves out of the scope of startFade()
  129.     //
  130.     // Without such incapsulation, "this" would evaluate to the window object 
  131.     // when the call was finally made by the timer
  132.     var localThis = this;
  133.     this.timer = setInterval (function() { localThis.tick() }, 13);
  134. }
  135.  
  136. /*
  137.  * Setter function for fade duration (floored at 250ms)
  138.  */
  139. Fader.prototype.setFadeTime  = function (fadeTime) {
  140.     this.fadeTime = fadeTime > 250 ? fadeTime : 250;
  141. }
  142.  
  143. /*
  144.  * tick does all the incremental work
  145.  *
  146.  * Every time this is hit by the timer, we calculate and apply
  147.  * a new opacity value on our target element; eventually this will hit 1 (on
  148.  * fade-in) or 0 (on fade-out)
  149.  */
  150. Fader.prototype.tick = function () {    
  151.     var T;
  152.     var ease;
  153.     var time = (new Date).getTime();
  154.     
  155.     // Calculate the time delta since the fade started
  156.     T = limit_3(time-this.startTime, 0, this.fadeTime);
  157.     
  158.     // The fade is over -- clear the timer, making this the last iteration
  159.     if (T >= this.fadeTime) {
  160.         clearInterval (this.timer);
  161.         this.timer = null;
  162.         this.now = this.to;
  163.         // invoke our callback, if one was set.
  164.         if (this.doneNotification) {
  165.             var localThis = this;
  166.             setTimeout(function() { localThis.doneNotification(); }, 0);
  167.         }
  168.     } else {
  169.         ease = 0.5 - (0.5 * Math.cos(Math.PI * T / this.fadeTime));
  170.         this.now = computeNextFloat (this.from, this.to, ease);
  171.     }
  172.     
  173.     // Set the opacity of the fading element; over repeated ticks, this.now will
  174.     // go up (fade in) or down (fade out)
  175.     this.element.style.opacity = this.now;
  176. }
  177.  
  178. /*
  179.  * support functions for tick operation
  180.  */
  181. function limit_3 (a, b, c) {
  182.     return a < b ? b : (a > c ? c : a);
  183. }
  184.  
  185. function computeNextFloat (from, to, ease) {
  186.     return from + (to - from) * ease;
  187. }
  188.  
  189.  
  190. // End Fader object definition
  191.  
  192.  
  193.  
  194. /*
  195.  **********************************************************************
  196.  * TransitionFader object fades between 2 of x number of DOM elements *
  197.  **********************************************************************
  198.  */
  199.  
  200. /*
  201.  * TransitionFader constructor.  Parameters:
  202.  * - elements: Should be an array; use Fader object to fade a single element in/out
  203.  * - fadeTime: How long (in ms) the fades should take
  204.  * - inDelay: Pause (in ms) between transition fades (see setFadeDelay())
  205.  *
  206.  * As with Fader, all the methods should be called through an instance variable
  207.  */
  208. function TransitionFader (elements, fadeTime, inDelay) {
  209.     // Create two fader objects: incoming and outgoing
  210.     // Set prepNextFade as a callback for one of the faders, so we can cycle
  211.     // through the elements array as a fade completes
  212.     var localThis = this;
  213.     this.inFader = new Fader(elements[0], function() { localThis.prepNextFade(); }, fadeTime);
  214.     this.outFader = new Fader(null, null, fadeTime);
  215.     
  216.     // All the elements we wish to transition between
  217.     // We do two at a time (one in / one out), but we can move through 
  218.     // an array of any size (see prepNextFade()).
  219.     this.elements = elements;
  220.     this.currentIndex = 0;
  221.     this.setFadeDelay(inDelay);
  222.     
  223.     this.paused = false;
  224.     this.interrupted = false;
  225. }
  226.  
  227. TransitionFader.prototype.start = function () {
  228.     this.inFader.fadeIn();
  229. }
  230.  
  231. TransitionFader.prototype.prepNextFade = function () {
  232.     // Before swapping the faders, make sure the element on its way out
  233.     // is completely gone; we do this in case the in and out Faders get
  234.     // too far out of sync (since this callback is only tied to inFader)
  235.     if (this.outFader.element) {
  236.         this.outFader.element.style.opacity = 0;
  237.     }
  238.     
  239.     // Swap the faders around; the element that was just faded in
  240.     // will now be faded out
  241.     var temp = this.outFader;
  242.     this.outFader = this.inFader;
  243.     this.inFader = temp;
  244.         
  245.     // Select a new element to be faded in.
  246.     this.currentIndex = (this.currentIndex + 1) % this.elements.length;
  247.     this.inFader.element = this.elements[this.currentIndex];
  248.  
  249.     // Queue up the next transition using the delay property
  250.     var localThis = this;
  251.     window.setTimeout(function() { localThis.fade(); }, this.delay);
  252. }
  253.  
  254. /*
  255.  * Do the fades
  256.  *
  257.  * We check the paused flag in case the delayed call from prepNextFade was
  258.  * already in place before paused was set to true: if so, we postpone the fade
  259.  * and set the interrupted flag (see resume())
  260.  */
  261. TransitionFader.prototype.fade = function () {
  262.     if (this.paused) {
  263.         this.interrupted = true;
  264.         return;
  265.     }
  266.     this.inFader.fadeIn();
  267.     this.outFader.fadeOut();
  268.     this.fading = true;
  269. }
  270.  
  271. TransitionFader.prototype.getFadeTime = function () {
  272.     return this.inFader.fadeTime;
  273. }
  274.  
  275. TransitionFader.prototype.setFadeTime = function (newFadeTime) {
  276.     this.inFader.setFadeTime(newFadeTime);
  277.     this.outFader.setFadeTime(newFadeTime);
  278. }
  279.  
  280. /*
  281.  * Setter for the delay between fades (floored at 500ms)
  282.  */
  283. TransitionFader.prototype.setFadeDelay = function (inDelay) {
  284.     this.delay = inDelay > 500 ? inDelay : 500;
  285. }
  286.  
  287. /*
  288.  *    Set the paused flag, so the transition timer stops the next time it is hit
  289.  */
  290. TransitionFader.prototype.pause = function () {
  291.     this.paused = true;
  292. }
  293.  
  294. /*
  295.  * If we are in the middle of a fade, setting paused to false will allow
  296.  * the timers to continue as usual; iff a fade was postponed, we need to 
  297.  * explicitly kick things off again
  298.  */
  299. TransitionFader.prototype.resume = function () {
  300.     this.paused = false;
  301.     if (this.interrupted) {
  302.         this.interrupted = false;
  303.         this.fade();
  304.     }
  305. }